﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
using System.Runtime.Serialization;
using System;
using System.Xml.Serialization;
using System.Threading;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BrainTechLLC.ThreadSafeObjects;

namespace BrainTechLLC
{
    [Serializable]
    [DataContract]
    public class MoveCommand : Command, ICommandInfo
    {
        public override string HelpText { get { return "Moves north, south, east, or west"; } }
        public override string Example { get { return "S"; } }

        public static List<string> Words = new List<string>() { "N", "S", "E", "W" };

        public override List<string> FirstWords { get { return Words; } }

        public override CommandToPerform CreateCommand(IGameThing source, string first, string input)
        {
            GeneralDirection dir = GeneralDirection.None;
            switch (first.ToUpper())
            {
                case "N": dir = GeneralDirection.N; break;
                case "S": dir = GeneralDirection.S; break;
                case "E": dir = GeneralDirection.E; break;
                case "W": dir = GeneralDirection.W; break;
            }

            CommandToPerform cmd = new CommandToPerform()
            {
                ApplyToID = source.AssociatedID,
                Arguments = new PropertyData(new IdentifierAndValue() { Identifier = "Direction", Value = dir }),
                CheckPermittedMethod = new Method("MoveCommand", "CheckCanMove"),
                PerformMethod = new Method("MoveCommand", "Move")
            };

            return cmd;
        }

        public override bool CheckIsValid(IGameThing source, string first, string input)
        {
            return true;
        }

        [MethodVisible]
        public static string CheckCanMove(CommandToPerform cmd, IGameThing source, IGameThing target, PropertyData args, PropertyData modifyingArguments)
        {
            GameChar gameThing = source as GameChar;

            if (gameThing.IsDead)
                return "You are dead!";

            GeneralDirection direction = args.GetArgument<GeneralDirection>("Direction");
            int n = gameThing.ThingProperties.Read<int>("MovementPoints");
            if (n <= 0) return "You have no movement points left.";

            GameLocation loc = gameThing.GameLocation;
            LocationEdge edge = loc.Edges[(int)direction];

            if (edge == null || edge.ToGameLocation == null)
                return "You cannot move that direction.";
                        
            return string.Empty;
        }

        [MethodVisible]
        public static List<PerceivedEventAndAudience> Move(CommandToPerform cmd, IGameThing source, IGameThing target, PropertyData args, PropertyData modifyingArguments)
        {
            GameChar gameThing = source as GameChar;
            GeneralDirection direction = args.GetArgument<GeneralDirection>("Direction");
            List<PerceivedEventAndAudience> events = null;

            if (gameThing != null)
            {
                events = new List<PerceivedEventAndAudience>();
                GameLocation loc = gameThing.GameLocation;
                LocationEdge edge = loc.Edges[(int)direction];

                long newLocationId=edge.ToLocationId;
                GameLocation newLocation = newLocationId.Find<GameLocation>();
                GameLocation oldLocation = WorldMap.Instance.ToLocation(gameThing.AssociatedID, newLocationId);
                gameThing.SetLocationInt(newLocation.XYZ, newLocation.AssociatedID);
                gameThing.EnqueueModification(CommandFactory.AddOrSubtractFromProperty("MovementPoints", -4));
                EventToPerceive evtLeave = CreateMoveObservableLeaveEvent(source, oldLocation, direction);
                EventToPerceive evtEnter = CreateMoveObservableEnterEvent(source, newLocation, direction);
                events.Add(new PerceivedEventAndAudience() { Audience = oldLocation.Things, Event = evtLeave });
                events.Add(new PerceivedEventAndAudience() { Audience = newLocation.Things, Event = evtEnter });

                if (cmd != null && gameThing.Settings.GetArgument<bool>("LookAfterMove"))
                {
                    cmd.AdditionalCommand = CommandFactory.CreateLook(gameThing.AssociatedID, string.Empty);                    
                }
            }

            return events;
        }

        [MethodVisible]
        public static EventToPerceive CreateMoveObservableLeaveEvent(IAssociatedID source, GameLocation oldLocation, GeneralDirection direction)
        {
            return new GeneralEventToPerceive()
            {
                ShowToObservers = new Event(string.Format("~source leaves to the {0}.", direction.DirectionText()), ViewType.See),
                Sources = new AssociatedIDCollection(source.AssociatedID)
            };
        }

        [MethodVisible]
        public static EventToPerceive CreateMoveObservableEnterEvent(IAssociatedID source, GameLocation newLocation, GeneralDirection direction)
        {
            return new GeneralEventToPerceive()
            {
                ShowToSource = new Event(string.Format("You move {0}.", direction.DirectionText()), ViewType.See),
                ShowToObservers = new Event(string.Format("~source enters from the {0}.", direction.ReverseDirection().DirectionText()), ViewType.See),
                Sources = new AssociatedIDCollection(source.AssociatedID)
            };
        }

    }

    public static partial class CommandFactory
    {
        [MethodVisible]
        public static CommandToPerform CreateMove(long source, GeneralDirection dir)
        {
            CommandToPerform cmd = new CommandToPerform()
            {
                ApplyToID = source,
                Arguments = new PropertyData(new IdentifierAndValue() { Identifier = "Direction", Value = dir }),
                CheckPermittedMethod = new Method("MoveCommand", "CheckCanMove"),
                PerformMethod = new Method("MoveCommand", "Move")
            };
            return cmd;
        }
    }
}
